チュートリアル動画にしたがってCloudflare Workersを使ってみた

チュートリアル動画にしたがってCloudflare Workersを使ってみた

Clock Icon2024.07.30

こんちには。

データ事業本部 インテグレーション部 機械学習チームの中村( @nokomoro3 )です。

本記事では以下の記事に引き続き、Cloudflare Workersを触ってみたいと思います。

https://dev.classmethod.jp/articles/vite-project-deploy-cloudflare-pages/

以下の公式YouTubeを参考にしており、半分くらい(32:50)のところまでの内容を本記事で扱っています。

https://www.youtube.com/watch?v=H7Qe96fqg1M

Cloudflare Workersとは

Cloudflare Workersは、Cloudflareが提供するサーバーレスのエッジコンピューティングプラットフォームです。
APIの作成やコンテンツの動的生成や変更などが可能で、Cloudflareの他のサービス(KV、D1データベース、R2ストレージなど)との統合も可能です。
従来のサーバーサイドプログラミングの多くのユースケースをカバーしつつ、エッジコンピューティングの利点を活かした高性能なアプリケーション開発を可能にします。

使用環境

Windows 10のホスト側で作業します。
また各コマンドはPowershellを使って実行しています。

事前準備

以下を事前に実施しておく前提です。

npmをpnpmにするとコケることがありましたので、npmを使っています。

使ってみた

プロジェクトの作成

以下を実行すると設定値について聞かれますので、ターミナルで入力をして進めます。

npm create cloudflare

プロジェクト名(アプリケーション名)を設定します。

╰ In which directory do you want to create your application? also used as application name
  ./workers-getting-started

Workerのタイプを選択します。Pythonもあるようですが、今回はグッとこらえてオーソドックスな一番上の「"Hello World" Worker」を選択します。

╰ What type of application do you want to create?
  ● "Hello World" Worker
  ○ "Hello World" Worker (Python)
  ○ Website or web app
  ○ Example router & proxy Worker
  ○ Scheduled Worker (Cron Trigger)
  ○ Queue consumer & producer Worker
  ○ Co-ordination / multiplayer API (using Durable Objects)
  ○ API starter (OpenAPI compliant)
  ○ Worker built from a template hosted in a git repository

TypeScriptは「Yes」を選択します。

╰ Do you want to use TypeScript?
  Yes / No

Gitによるバージョン管理も「Yes」にしておきます。

╰ Do you want to use git for version control?
  Yes / No

デプロイは手動で行いたいですので、以下は一旦Noとしておきます。

╰ Do you want to deploy your application?
  Yes / No

最終的には以下のようにターミナルに表示されました。

using create-cloudflare version 2.22.3

╭ Create an application with Cloudflare Step 1 of 3
│
├ In which directory do you want to create your application?
│ dir ./workers-getting-started
│
├ What type of application do you want to create?
│ type "Hello World" Worker
│
├ Do you want to use TypeScript?
│ yes typescript
│
├ Copying template files
│ files copied to project directory
│
├ Updating name in `package.json`
│ updated `package.json`
│
├ Installing dependencies
│ installed via `npm install`
│
╰ Application created

╭ Configuring your application for Cloudflare Step 2 of 3
│
├ Installing @cloudflare/workers-types
│ installed via npm
│
├ Adding latest types to `tsconfig.json`
│ added @cloudflare/workers-types/2023-07-01
│
├ Retrieving current workerd compatibility date
│ compatibility date 2024-07-25
│
├ Do you want to use git for version control?
│ yes git
│
├ Initializing git repo
│ initialized git
│
├ Committing new files
│ git commit
│
╰ Application configured

╭ Deploy with Cloudflare Step 3 of 3
│
├ Do you want to deploy your application?
│ no deploy via `npm run deploy`
│
├  APPLICATION CREATED  Deploy your application with npm run deploy
│
│ Navigate to the new directory cd workers-getting-started
│ Run the development server npm run start
│ Deploy your application npm run deploy
│ Read the documentation https://developers.cloudflare.com/workers
│ Stuck? Join us at https://discord.cloudflare.com
│
╰ See you again soon!

作成されたプロジェクトには以下のようなファイル・ディレクトリが格納されています。

node_modules/
src/
test/
.editorconfig
.prettierrc
package-lock.json
package.json
tsconfig.json
vitest.config.mts
worker-configuration.d.ts
wrangler.toml

package.jsonscript のところを確認すると、 wrangler というコマンドで操作されていることが分かります。

{
  "name": "workers-getting-started",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "deploy": "wrangler deploy",
    "dev": "wrangler dev",
    "start": "wrangler dev",
    "test": "vitest",
    "cf-typegen": "wrangler types"
  },
  "devDependencies": {
    "@cloudflare/vitest-pool-workers": "^0.4.5",
    "@cloudflare/workers-types": "^4.20240725.0",
    "typescript": "^5.5.2",
    "vitest": "1.5.0",
    "wrangler": "^3.60.3"
  }
}

wranglerとは

wranglerはCloudflareのアプリケーションを管理するためのコマンドラインツールで、Workers、Pages以外にもD1やR2、KVなども管理できます。

npx wrangler を実行すれば各操作を実行することができます。

npx wrangler help

# wrangler
# 
# COMMANDS
#   wrangler docs [command]            📚 Open Wrangler's command documentation in your browser
# 
#   wrangler init [name]               📥 Initialize a basic Worker
#   wrangler dev [script]              👂 Start a local server for developing your Worker
#   wrangler deploy [script]           🆙 Deploy a Worker to Cloudflare  [aliases: publish]
#   wrangler deployments               🚢 List and view the current and past deployments for your Worker [open beta]
#   wrangler rollback [deployment-id]  🔙 Rollback a deployment for a Worker [open beta]
#   wrangler delete [script]           🗑  Delete a Worker from Cloudflare
#   wrangler tail [worker]             🦚 Start a log tailing session for a Worker
#   wrangler secret                    🤫 Generate a secret that can be referenced in a Worker
#   wrangler types [path]              📝 Generate types from bindings and module rules in configuration
# 
#   wrangler kv                        🗂️  Manage Workers KV Namespaces
#   wrangler queues                    🇶  Manage Workers Queues
#   wrangler r2                        📦 Manage R2 buckets & objects
#   wrangler d1                        🗄  Manage Workers D1 databases
#   wrangler vectorize                 🧮 Manage Vectorize indexes [open beta]
#   wrangler hyperdrive                🚀 Manage Hyperdrive databases
#   wrangler pages                     ⚡️ Configure Cloudflare Pages
#   wrangler mtls-certificate          🪪  Manage certificates used for mTLS connections
#   wrangler pubsub                    📮 Manage Pub/Sub brokers [private beta]
#   wrangler dispatch-namespace        🏗️  Manage dispatch namespaces
#   wrangler ai                        🤖 Manage AI models
# 
#   wrangler login                     🔓 Login to Cloudflare
#   wrangler logout                    🚪 Logout from Cloudflare
#   wrangler whoami                    🕵️  Retrieve your user information
# 
# GLOBAL FLAGS
#   -j, --experimental-json-config  Experimental: support wrangler.json  [boolean]
#   -c, --config                    Path to .toml configuration file  [string]
#   -e, --env                       Environment to use for operations and .env files  [string]
#   -h, --help                      Show help  [boolean]
#   -v, --version                   Show version number  [boolean]
# 
# Please report any issues to https://github.com/cloudflare/workers-sdk/issues/new/choose

wranglerを使ってCloudflareにログイン

アプリケーションをデプロイするためには、ログインが必要です。以下のコマンドを実行します。

npx wrangler login

#  ⛅️ wrangler 3.67.1
# -------------------
# 
# Attempting to login via OAuth...
# Opening a link in your default browser: ...(以降略)

するとブラウザ側に遷移するので、問題なければ以下の「Allow」を押下します。

get-start-cloudflare-workers_2024-07-30-12-02-41

以下の画面が出ればOKです。こちらのタブは閉じてしまって大丈夫です。

get-start-cloudflare-workers_2024-07-30-12-03-58

ターミナル側に戻ると「Successfully logged in.」と表示されていると思います。

ログインに失敗する場合は以下の対応を試してみてください(私はここでハマりました)。

https://blog.y-yuki.net/entry/2021/04/18/003000

ログイン後は、ログイン状況を以下のコマンドで確認できます。

npx wrangler whoami

#  ⛅️ wrangler 3.67.1
# -------------------
# 
# Getting User settings...
# 👋 You are logged in with an OAuth Token, associated with the email ****!
# ┌─────────────────────────────────────────┬──────────────────────────────────┐
# │ Account Name                            │ Account ID                       │
# ├─────────────────────────────────────────┼──────────────────────────────────┤
# │ ****                                    │ ****                             │
# └─────────────────────────────────────────┴──────────────────────────────────┘
# 🔓 Token Permissions: If scopes are missing, you may need to logout and re-login.
# Scope (Access)
# - account (read)
# - user (read)
# - workers (write)
# - workers_kv (write)
# - workers_routes (write)
# - workers_scripts (write)
# - workers_tail (read)
# - d1 (write)
# - pages (write)
# - zone (read)
# - ssl_certs (write)
# - ai (write)
# - queues (write)
# - offline_access

デプロイ

ログインすると、アプリケーションのデプロイを行うことができます。

npm run deploy

# > [email protected] deploy
# > wrangler deploy
# 
# 
#  ⛅️ wrangler 3.67.1
# -------------------
# 
# Total Upload: 0.19 KiB / gzip: 0.16 KiB
# Uploaded workers-getting-started (1.22 sec)
# Published workers-getting-started (3.85 sec)
#   https://***
# Current Deployment ID: ****
# Current Version ID: ****
# 
# 
# Note: Deployment ID has been renamed to Version ID. Deployment ID is present to maintain compatibility with the previous behavior of this command. This output will change in a future version of Wrangler. To learn more visit: https://developers.cloudflare.com/workers/configuration/versions-and-deployments
#

表示されたURLにアクセスすると、Hello Worldを確認することができました。

get-start-cloudflare-workers_2024-07-30-12-10-54

Cloudflareの管理コンソールでもアプリケーションが作成されていることが確認できます。

get-start-cloudflare-workers_2024-07-30-12-14-12

開発用サーバーの起動

開発用サーバーを起動する方法も確認しておきます。

npm start

# > [email protected] start
# > wrangler dev
# 
# 
#  ⛅️ wrangler 3.67.1
# -------------------
# 
# ⎔ Starting local server...
# [wrangler:inf] Ready on http://127.0.0.1:8787
# ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
# │ [b] open a browser, [d] open Devtools, [l] turn off local mode, [c] clear console, [x] to exit  
# ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

http://127.0.0.1:8787 にアクセスすると、ローカル開発サーバーにアクセスできます。実際にはminiflareと呼ばれるツールを使ってローカル開発サーバーをセットアップしているようです。
miniflareはオープンソースのJavaScriptランタイムであり、以下から確認することができます。

https://github.com/cloudflare/workers-sdk/tree/main/packages/miniflare

コードの編集

アプリケーションのメインは src/index.ts にあります。以下のレスポンスが今ブラウザに表示されています。

src/index.ts
export default {
	async fetch(request, env, ctx): Promise<Response> {
		return new Response('Hello World!');
	},
} satisfies ExportedHandler<Env>;

Hello World を変更し、開発サーバーをリロードすると、レスポンスが書き変わっていることが分かります。

src/index.ts
export default {
	async fetch(request, env, ctx): Promise<Response> {
		return new Response('Hello, Youtube!');
	},
} satisfies ExportedHandler<Env>;

ローカルモードのオフ

ローカル環境では、Cloudflareの他サービス(D1やKVなど)にアクセスするような動作確認が難しいのですが、ローカルモードをオフにすることでCloudflare固有のプレビューサーバーでテストバージョンを動かすようなことが可能です。

ローカル開発サーバーを動かしている状態で以下のようなメッセージが出ていると思いますが、ここで l キーを押下します。

# ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
# │ [b] open a browser, [d] open Devtools, [l] turn off local mode, [c] clear console, [x] to exit  
# ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

⎔ Shutting down local server...
Total Upload: 5.42 KiB / gzip: 1.73 KiB

この設定だけで、プレビューサーバーで動作するように変わっています。

実際ブラウザの開発ツールでレスポンスヘッダを確認すると、元々は以下でしたが、

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Content-Type: text/plain;charset=UTF-8
Content-Encoding: gzip

ローカルモードをオフとすると以下のように CF-RAY が付与されたり Server: cloudflare に変化しています。CF-RAYはリクエストの一意の識別子のようなものとなっています。

HTTP/1.1 200 OK
Transfer-Encoding: chunked
Date: Tue, 30 Jul 2024 03:41:12 GMT
Content-Type: text/plain;charset=UTF-8
Content-Encoding: gzip
Server: cloudflare
Vary: Accept-Encoding
alt-svc: h3=":443"; ma=86400
CF-RAY: 8ab254e6ab5046b5-SIN
...(以降略)

リクエストのダンプ

コード上でリクエストに関する詳細情報をダンプすることもできます。

src/index.ts
export default {
	async fetch(request, env, ctx): Promise<Response> {
		console.log(JSON.stringify(request.cf))
		return new Response('Hello, Youtube!');
	},
} satisfies ExportedHandler<Env>;

ローカルサーバーの標準出力としてターミナルから以下のように取得できます。

{
    "clientTcpRtt": 7,
    "longitude": "***",
    "httpProtocol": "HTTP/1.1",
    "tlsCipher": "AEAD-AES256-GCM-SHA384",
    "continent": "AS",
    "asn": 2518,
    "clientAcceptEncoding": "gzip, deflate, br, zstd",
    "country": "JP",
    "verifiedBotCategory": "",
    "tlsClientAuth": {
        "certIssuerDNLegacy": "",
        "certIssuerSKI": "",
        "certSubjectDNRFC2253": "",
        "certSubjectDNLegacy": "",
        "certFingerprintSHA256": "",
        "certNotBefore": "",
        "certSKI": "",
        "certSerial": "",
        "certIssuerDN": "",
        "certVerified": "NONE",
        "certNotAfter": "",
        "certSubjectDN": "",
        "certPresented": "0",
        "certRevoked": "0",
        "certIssuerSerial": "",
        "certIssuerDNRFC2253": "",
        "certFingerprintSHA1": ""
    },
    "tlsExportedAuthenticator": {
        "clientFinished": "****",
        "clientHandshake": "****",
        "serverHandshake": "****",
        "serverFinished": "****"
    },
    "tlsVersion": "TLSv1.3",
    "city": "Tokyo",
    "timezone": "Asia/Tokyo",
    "colo": "NRT",
    "tlsClientHelloLength": "386",
    "edgeRequestKeepAliveStatus": 1,
    "postalCode": "****",
    "region": "Tokyo",
    "latitude": "****",
    "requestPriority": "",
    "regionCode": "13",
    "asOrganization": "****",
    "tlsClientExtensionsSha1": "****",
    "tlsClientRandom": "****",
    "botManagement": {
        "corporateProxy": false,
        "verifiedBot": false,
        "jsDetection": {
            "passed": false
        },
        "staticResource": false,
        "detectionIds": {},
        "score": 99
    }
}

postalCodeやISPなどの情報を確認することができます。

動画内でも botManagement というオブジェクトは興味深いものとして取り上げられており、この中の score でボットかどうかを判断する材料とすることができます。(99は99%の確率でボットではないという意味です)

Bindingsについて

動画内では、 export interface Env というものがメインのコードにあるようですが、現状は worker-configuration.d.ts という別ファイルに定義されています。

worker-configuration.d.ts
// Generated by Wrangler
// After adding bindings to `wrangler.toml`, regenerate this interface via `npm run cf-typegen`
interface Env {
}
src/index.ts
/**
 * Welcome to Cloudflare Workers! This is your first worker.
 *
 * - Run `npm run dev` in your terminal to start a development server
 * - Open a browser tab at http://localhost:8787/ to see your worker in action
 * - Run `npm run deploy` to publish your worker
 *
 * Bind resources to your worker in `wrangler.toml`. After adding bindings, a type definition for the
 * `Env` object can be regenerated with `npm run cf-typegen`.
 *
 * Learn more at https://developers.cloudflare.com/workers/
 */

export default {
	async fetch(request, env, ctx): Promise<Response> {
		console.log(JSON.stringify(request.cf))
		return new Response('Hello, Youtube!');
	},
} satisfies ExportedHandler<Env>;

公式ドキュメントで使い方を確認してみます。

https://developers.cloudflare.com/workers/runtime-apis/bindings/

EnvはBindingsに使用されるもので、Workersから他のCloudflareのサービスに接続するためのオブジェクトで、以下に記載がありました。

たとえばR2バケットを使用したい場合は、まず wrangler.toml に以下のように記載し、

main = "./src/index.js"
r2_buckets = [
  { binding = "MY_BUCKET", bucket_name = "<MY_BUCKET_NAME>" }
]

その後、コード側で env オブジェクトを使って以下のように env.MY_BUCKET という形でアクセスできるようになるようです。

export default {
  async fetch(request, env) {
    const key = url.pathname.slice(1);
    await env.MY_BUCKET.put(key, request.body);
    return new Response(`Put ${key} successfully!`);
  }
}

Handlersについて

生成されたコードでは fetch というメソッドが定義されていますが、これはFetch Handlerと言われるもので、これ以外にも様々なHandlerを記述することができます。

https://developers.cloudflare.com/workers/runtime-apis/handlers/

Handlerは外部入力を受け取って処理するWorkerのメソッドで、Workerの外部から呼び出すことができます。

例えばFetch HandlerはHTTPリクエストを受け取り、レスポンスを返すことができます。

その他にもスケジュールされた処理を扱うScheduled Handlerなどがあります。

export default {
  async scheduled(event, env, ctx) {
    ctx.waitUntil(doSomeTaskOnASchedule());
  },
}

JSONレスポンスを返す方法

JSONのレスポンスをFetch Handlerで返すには以下のように記載します。

src/index.ts
export default {
	async fetch(request, env, ctx): Promise<Response> {
		return new Response(JSON.stringify({ hello: "World" }), {
			headers: {
				'Content-Type': 'application/json'
			}
		});
	},
} satisfies ExportedHandler<Env>;

これらはHonoというフレームワークを使えば、より簡単に実行できるようですが、今回はベーシックな方法で試してみます。

最後にデプロイして動作確認をします。

npm run deploy

get-start-cloudflare-workers_2024-07-30-17-43-26

CLIからもアクセスしてみます。

curl {デプロイ先URL} | jq

#   % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
#                                  Dload  Upload   Total   Spent    Left  Speed  
# 100    17  100    17    0     0     33      0 --:--:-- --:--:-- --:--:--    33 
# {
#   "hello": "World"
# }

ただしくリクエストが取得できていることがわかりました。

wrangler.tomlについて

wrangler.toml にはWorkersアプリケーションの様々な設定を記載することができます。

wrangler.toml
#:schema node_modules/wrangler/config-schema.json
name = "workers-getting-started"
main = "src/index.ts"
compatibility_date = "2024-07-25"
compatibility_flags = ["nodejs_compat"]

name はアプリケーション名、 main はアプリケーションのメインファイルを指定することができます。

compatibility_date は互換性を保つための情報で、動画内でも最も重要な情報と説明されています。
プロジェクトのWorkersのランタイムバージョンを管理するためのもので、特定の日付のバージョンにランタイムをロックするような動作をします。

この設定値やランタイムのChange historyについては以下にも記載されているので、詳細は以下をご確認ください。

https://developers.cloudflare.com/workers/configuration/compatibility-dates/

まとめ

いかがでしたでしょうか。この動画の続きはHonoフレームワークを使ったもののようですので、こちらも引き続き記事にできたらと思っています。

本記事がCloudflare Workersをお使いになる方の参考になれば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.